/*
 * Copyright 2009 Max Kugland
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.splink.deepsplink.deeplink {
	import org.splink.deepsplink.config.ConfigData;
	import org.splink.deepsplink.interceptor.IInterceptor;
	import org.splink.deepsplink.navigation.INavigation;
	import org.splink.deepsplink.page.params.PageParameterUtil;
	import org.splink.deepsplink.page.request.IRequestProcessor;
	import org.splink.deepsplink.processor.IProcessor;
	import org.splink.library.utils.ArrayUtils;

	/**
	 * @inheritDoc
	 * 
	 * <code>DeeplinkController</code> is the default <code>IDeeplinkController</code> implementation.
	 * It's <code>gotoPage</code> method receives page change requests and delegates them to it's
	 * <code>IDeeplinkStrategy</code>. Afterwards it waits for the strategy's notification that the
	 * adressbar changed. When that notification arrives, the page change request is inspected by the
	 * previously added <code>IInterceptor</code> implementations and depending on the result carried out.
	 * Finally all previously added <code>INavigation</code> implementations are notified about the page change.
	 * Additionally the changes are recorded in a history and all previously added <code>IProcessor</code> 
	 * implementations learn about the page change. 
	 * Wherever the page change request originates (within actionscript code or the browser's adressbar) the
	 * above operations happen. 
	 * 
	 * @see org.splink.deepsplink.deeplink.IDeeplinkStrategy
	 * 
	 * @author Max Kugland
	 */
	final public class DeeplinkController implements IDeeplinkController {
		private static const MAX_HISTORY_SIZE : uint = 100;
		private var _homeId : String;
		private var _requestProcessor : IRequestProcessor;		private var _deeplinkStrategy : IDeeplinkStrategy;
		private var _interceptors : Array = new Array();
		private var _navigation : Array = new Array();
		private var _processors : Array = new Array();		private var _history : Array = new Array();

		/**
		 * @param homeId the home id which is called if no id is specified
		 * @param pageController an <code>IPageController</code> implementation
		 */
		public function DeeplinkController(config : ConfigData, requestProcessor : IRequestProcessor, deeplinkStrategy : IDeeplinkStrategy) {
			_homeId = config.homeData.id;
			_requestProcessor = requestProcessor;
			_deeplinkStrategy = deeplinkStrategy;
			_deeplinkStrategy.setCallback(onAddressChange);
		}

		/**
		 * @inheritDoc
		 */
		public function start() : void {
			_deeplinkStrategy.start();
		}

		/**
		 * @inheritDoc
		 */
		public function addInterceptor(interceptor : IInterceptor) : void {
			if(!ArrayUtils.contains(_interceptors, interceptor)) {
				_interceptors.push(interceptor);
			}
		}

		/**
		 * @inheritDoc
		 */
		public function removeInterceptor(interceptor : IInterceptor) : void {
			ArrayUtils.remove(_interceptors, interceptor);
		}

		/**
		 * @inheritDoc
		 */
		public function addProcessor(processor : IProcessor) : void {
			if(!ArrayUtils.contains(_processors, processor)) {
				_processors.push(processor);
			}
		}

		/**
		 * @inheritDoc
		 */
		public function removeProcessor(processor : IProcessor) : void {
			ArrayUtils.remove(_processors, processor);
		}

		/**
		 * @inheritDoc
		 */
		public function gotoPage(id : String, pageParams : Array = null) : void {
			_deeplinkStrategy.gotoPage(new Request(checkForHome(id), pageParams).fullId);
		}

		/**
		 * @inheritDoc
		 */
		public function registerNavigation(navigation : INavigation) : void {
			if(!ArrayUtils.contains(_navigation, navigation)) {
				if(_deeplinkStrategy.getCurrentAddress() != "") {
					var id : String = PageParameterUtil.extractId(_deeplinkStrategy.getCurrentAddress());
					var pars : Array = PageParameterUtil.extractParams(_deeplinkStrategy.getCurrentAddress());
					navigation.select(id, pars);
				}
				_navigation.push(navigation);
			}
		}

		/**
		 * @inheritDoc
		 */
		public function unregisterNavigation(navigation : INavigation) : void {
			ArrayUtils.remove(_navigation, navigation);
		}

		private function selectNavigation(request : Request, changed : Boolean = true) : void {
			for each (var navigation : INavigation in _navigation) {
				navigation.select(request.id, request.params, changed);
			}
		}

		private function intercept(request : Request) : Boolean {
			for each (var interceptor : IInterceptor in _interceptors) {
				if(interceptor.intercept(request.id, request.params)) {
					return true;
				}
			}
			
			return false;
		}

		private function applyProcessors(request : Request) : void {
			for each (var processor : IProcessor in _processors) {
				processor.process(request.id, request.params);
			}
		}

		private function checkForHome(id : String) : String {
			return (id == "") ? _homeId : id;
		}

		/**
		 * @inheritDoc
		 */
		public function onAddressChange() : void {
			var request : Request = Request.fromFullId(_deeplinkStrategy.getCurrentAddress(), _homeId);
			
			if(getLastId() == request.fullId) return;
			if(intercept(request)) return;
			
			selectNavigation(request);
			_requestProcessor.gotoPage(request.id, request.params);

			applyProcessors(request);
			addToHistory(request);
		}

		private function getLastId() : String {
			return _history[_history.length - 1];
		}

		private function addToHistory(request : Request) : void {
			_history.push(request.fullId);
			if(_history.length > MAX_HISTORY_SIZE) {
				_history.shift();
			}
		}

		/**
		 * @inheritDoc
		 */
		public function finalize() : void {
			_deeplinkStrategy.finalize();
			_requestProcessor.finalize();
			_history = new Array();
			_interceptors = new Array();
			_navigation = new Array();
			_processors = new Array();
		}

		/**
		 * @inheritDoc
		 */
		public function get history() : Array {
			return _history;
		}
	}
}
